﻿namespace Serializator
{
    using System;
    using System.Reflection;
    using System.Collections;
    using Serializator.JSONObjects;

    /// <summary>
    /// JSON Serializator
    /// </summary>
    public class JSONSerializer : ISerializer
    {
        #region ISerializer Implementation
        /// <summary>
        /// Object serialization
        /// </summary>
        /// <param name="obj">Object</param>
        /// <returns>JSONObject</returns>
        public JSONObject Serialize(object obj)
        {
            if (obj == null)
                throw new ArgumentNullException();

            JSONObject jsonResult = ContainerSerializer(obj.GetType(), obj);

            return jsonResult;
        }

        /// <summary>
        /// Deserialization
        /// </summary>
        /// <param name="serializedValue"></param>
        /// <returns></returns>
        public object DeSerialize(JSONObject serializedValue)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region Private Logic

        /// <summary>
        /// Serialize composite
        /// </summary>
        /// <param name="type">Composite type</param>
        /// <param name="obj">Composite value</param>
        /// <returns>JSON Object</returns>
        private JSONObject ContainerSerializer(Type type, object obj)
        {
            if (type == null || obj == null)
                throw new ArgumentNullException();

            ///get all properties
            PropertyInfo[] propsInfo = type.GetProperties();
            ///create wrapper
            JSONObject container = JSONObjectFactory.GetJSONObjectInstance(typeof(JSONClassObject));

            foreach (PropertyInfo prop in propsInfo)
            {
                if (prop.PropertyType.IsClass && prop.PropertyType != typeof(string) && !prop.PropertyType.IsGenericType)
                {
                    container.EncodedStreamList.Add(ContainerSerializer(prop.PropertyType, prop.GetValue(obj, null)));
                }
                else
                {
                    if (prop.PropertyType.IsGenericType)
                    {
                        container.EncodedStreamList.Add(ListSerializer(prop, prop.GetValue(obj, null)));
                    }
                    else
                    {
                        container.EncodedStreamList.Add(ValueTypeSerializer(prop, prop.GetValue(obj, null)));
                    }
                }
            }

            return container;
        }


        /// <summary>
        /// Value type serialization
        /// </summary>
        /// <param name="prop">Property Info</param>
        /// <param name="v">Object value</param>
        /// <returns>Temporary container</returns>
        private JSONObject ValueTypeSerializer(PropertyInfo prop, object v)
        {
            if (prop == null || v == null)
                throw new ArgumentNullException();

            JSONObject container = JSONObjectFactory.GetJSONObjectInstance(typeof(JSONValueObject));

            string temp = String.Format("{0}:", prop.Name);
            var code = Type.GetTypeCode(prop.PropertyType);

            switch (code)
            {
                case TypeCode.String:
                case TypeCode.Boolean:
                case TypeCode.Char:
                    {
                        temp += String.Format("\"{0}\"", v.ToString());
                        break;
                    }
                default:
                    {
                        temp += v.ToString();
                        break;
                    }
            }
            container.Stream = temp;

            return container;
        }

        /// <summary>
        /// Array serialization
        /// </summary>
        /// <param name="prop">Property Info</param>
        /// <param name="obj">Object value</param>
        /// <returns>Temporary container</returns>
        private JSONObject ListSerializer(PropertyInfo prop, object obj)
        {
            if (prop == null || obj == null)
                throw new ArgumentNullException();

            JSONObject container = JSONObjectFactory.GetJSONObjectInstance(typeof(JSONArrayObject));

            foreach (object element in (IList)obj)
            {
                JSONObject arrayElementContainer = null;
                if (element.GetType().IsClass && element.GetType() != typeof(string) && !element.GetType().IsGenericType)
                {
                    arrayElementContainer = new JSONClassObject();
                    arrayElementContainer = ContainerSerializer(element.GetType(), element);
                }
                else
                {
                    arrayElementContainer = new JSONValueObject();
                    arrayElementContainer.Stream = element.ToString();
                }
                container.EncodedStreamList.Add(arrayElementContainer);
            }

            return container;
        }

        #endregion
    }
}
